<template>
  <div v-loading="loading" class="match-tree">
    <canvas
      ref="canvasRef"
      :width="canvasWidth"
      :height="canvasHeight"
    ></canvas>
    <el-button v-if="editable" type="primary" @click="saveAsImage" class=""
      >保存为图片</el-button
    >
  </div>
</template>

<script setup>
import {
  defineProps,
  ref,
  onMounted,
  nextTick,
  onUnmounted,
  defineExpose,
  defineEmits,
} from 'vue'
import { ElButton } from 'element-plus'
import { droneApi, obstacleApi, gobangApi } from '@/api/modules'

const apiHandle = {
  drone: droneApi,
  obstacle: obstacleApi,
  gobang: gobangApi,
}
const props = defineProps({
  activeTab: {
    type: String,
  },
  playType: {
    type: Number,
  },
  regNo: {
    type: String,
    default: '',
  },
  batchNo: {
    type: String,
    default: '',
  },
  groupCategory: {
    type: String,
    default: '',
  },
  editable: {
    type: Boolean,
    default: true,
  },
})

const treeData = ref([])
const canvasRef = ref(null)
const canvasWidth = ref(1200)
const canvasHeight = ref(800)
const loading = ref(true)
const hoverNode = ref(null)
const scale = ref(1)
const emit = defineEmits(['node-click'])
const resizeObserver = ref(null)

// 初始化ResizeObserver
// 渲染画布
const renderCanvas = () => {
  if (!canvasRef.value) return
  const ctx = canvasRef.value.getContext('2d')
  if (!ctx) return

  // 清空画布
  ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value)

  // 绘制对阵图
  treeData.value.forEach((node, index) => {
    const startX =
      canvasWidth.value - nodeConfig.width - nodeConfig.horizontalGap
    const startY =
      (index + 0.5) * nodeConfig.verticalGap * Math.pow(2, getMaxDepth() - 1)
    drawMatchTree(ctx, node, startX, startY, getMaxDepth() - 1)
  })
}

// 节点样式配置
const nodeConfig = {
  width: 120,
  height: 40,
  horizontalGap: 160,
  verticalGap: 80,
  backgroundColor: '#fff',
  winnerBackgroundColor: '#67c23a',
  borderColor: '#dcdfe6',
  textColor: '#303133',
  winnerTextColor: '#fff',
  scoreColor: '#409eff',
  lineColor: '#dcdfe6',
  fontSize: 14,
  borderRadius: 4,
}

// 计算树的深度
const getTreeDepth = (node) => {
  if (!node.children || node.children.length === 0) return 1
  return 1 + Math.max(...node.children.map((child) => getTreeDepth(child)))
}

// 计算树的总层数
const getMaxDepth = () => {
  return Math.max(...treeData.value.map((node) => getTreeDepth(node)))
}

// 绘制圆角矩形
const drawRoundRect = (ctx, x, y, width, height, radius) => {
  ctx.beginPath()
  ctx.moveTo(x + radius, y)
  ctx.lineTo(x + width - radius, y)
  ctx.arcTo(x + width, y, x + width, y + radius, radius)
  ctx.lineTo(x + width, y + height - radius)
  ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius)
  ctx.lineTo(x + radius, y + height)
  ctx.arcTo(x, y + height, x, y + height - radius, radius)
  ctx.lineTo(x, y + radius)
  ctx.arcTo(x, y, x + radius, y, radius)
  ctx.closePath()
}

// 判断点是否在矩形内
const isPointInRect = (x, y, rectX, rectY, rectWidth, rectHeight) => {
  return (
    x >= rectX &&
    x <= rectX + rectWidth &&
    y >= rectY &&
    y <= rectY + rectHeight
  )
}

// 获取鼠标在canvas中的坐标
const getMousePos = (canvas, event) => {
  const rect = canvas.getBoundingClientRect()
  return {
    x: event.clientX - rect.left,
    y: event.clientY - rect.top,
  }
}

// 检查鼠标是否悬停在节点上
const checkHoverNode = (mouseX, mouseY, node, x, y, level) => {
  if (
    isPointInRect(mouseX, mouseY, x, y, nodeConfig.width, nodeConfig.height)
  ) {
    return { node, x, y }
  }
  if (node && node.children) {
    const childrenGap = nodeConfig.verticalGap * Math.pow(2, level || 0)
    const childY1 = y - childrenGap / 2
    const childY2 = y + childrenGap / 2

    const leftResult = checkHoverNode(
      mouseX,
      mouseY,
      node.children[0],
      x - nodeConfig.horizontalGap,
      childY1,
      level - 1
    )
    if (leftResult) return leftResult

    if (node.children[1]) {
      const rightResult = checkHoverNode(
        mouseX,
        mouseY,
        node.children[1],
        x - nodeConfig.horizontalGap,
        childY2,
        level - 1
      )
      if (rightResult) return rightResult
    }
  }
  return null
}

// 绘制节点
const drawNode = (ctx, node, x, y) => {
  const isWinner = node.isWinner
  const isHovered = hoverNode.value?.node === node

  // 绘制背景
  ctx.fillStyle = isWinner
    ? nodeConfig.winnerBackgroundColor
    : nodeConfig.backgroundColor
  ctx.strokeStyle = isHovered ? '#409eff' : nodeConfig.borderColor
  ctx.lineWidth = isHovered ? 2 : 1
  drawRoundRect(
    ctx,
    x,
    y,
    nodeConfig.width,
    nodeConfig.height,
    nodeConfig.borderRadius
  )
  ctx.fill()
  ctx.stroke()

  // 绘制文字
  ctx.fillStyle = isWinner ? nodeConfig.winnerTextColor : nodeConfig.textColor
  ctx.font = `${nodeConfig.fontSize}px Arial`
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'

  const matchSequence = node.matchSequence || '待定'
  const scoreRank = node.scoreRank || ''

  // 绘制队伍ID
  ctx.fillText(
    matchSequence,
    x + nodeConfig.width / 2 - 20,
    y + nodeConfig.height / 2
  )

  // 绘制比分
  if (scoreRank !== '') {
    ctx.fillStyle = isWinner
      ? nodeConfig.winnerTextColor
      : nodeConfig.scoreColor
    ctx.fillText(
      scoreRank,
      x + nodeConfig.width / 2 + 20,
      y + nodeConfig.height / 2
    )
  }
}

// 绘制连接线
const drawConnector = (ctx, startX, startY, endX, endY) => {
  ctx.beginPath()
  ctx.strokeStyle = nodeConfig.lineColor
  ctx.lineWidth = 2

  // 水平线
  const midX = (startX + endX) / 2
  ctx.moveTo(startX, startY)
  ctx.lineTo(midX, startY)

  // 垂直线
  ctx.lineTo(midX, endY)

  // 水平线到目标节点
  ctx.lineTo(endX, endY)
  ctx.stroke()
}

// 递归绘制树
const drawTree = (ctx, node, x, y, level, totalLevels) => {
  drawNode(ctx, node, x, y)

  if (node.children && node.children.length > 0) {
    const childrenGap = nodeConfig.verticalGap * Math.pow(2, level)
    const childY1 = y - childrenGap / 2
    const childY2 = y + childrenGap / 2

    // 绘制左子节点
    drawTree(
      ctx,
      node.children[0],
      x - nodeConfig.horizontalGap,
      childY1,
      level - 1,
      totalLevels
    )

    // 绘制右子节点
    if (node.children[1]) {
      drawTree(
        ctx,
        node.children[1],
        x - nodeConfig.horizontalGap,
        childY2,
        level - 1,
        totalLevels
      )
    }

    // 绘制连接线
    drawConnector(
      ctx,
      x - nodeConfig.horizontalGap + nodeConfig.width,
      childY1 + nodeConfig.height / 2,
      x,
      y + nodeConfig.height / 2
    )

    if (node.children[1]) {
      drawConnector(
        ctx,
        x - nodeConfig.horizontalGap + nodeConfig.width,
        childY2 + nodeConfig.height / 2,
        x,
        y + nodeConfig.height / 2
      )
    }
  }
}

// 计算树的深度和宽度
// 常量定义
const SCALE_PRECISION = 0.01 // 缩放比例的精度
const SIZE_CHANGE_THRESHOLD = 5 // 尺寸变化的阈值（像素）

// 获取树结构的度量信息
const getTreeMetrics = () => {
  let maxDepth = 0
  let maxWidth = 0
  let totalNodes = 0
  let multiRootTreeMap = {}

  const calculateMetrics = (node, rootIndex, depth = 0) => {
    if (!node) return
    maxDepth = Math.max(maxDepth, depth)
    totalNodes++

    if (node.children) {
      const levelWidth = Math.pow(2, depth + 1)
      multiRootTreeMap[rootIndex] = Math.max(
        multiRootTreeMap[rootIndex] || 0,
        levelWidth
      )
      node.children.forEach((child) =>
        calculateMetrics(child, rootIndex, depth + 1)
      )
    }
  }

  treeData.value.forEach((node, index) => calculateMetrics(node, index))
  maxWidth = Object.values(multiRootTreeMap).reduce(
    (sum, value) => sum + value,
    0
  )
  return { maxDepth, maxWidth, totalNodes }
}

// 自动计算画布尺寸和缩放比例

const updateCanvasSize = () => {
  const { maxDepth, maxWidth, totalNodes } = getTreeMetrics()
  const container = canvasRef.value?.parentElement
  if (!container) return

  // 获取容器尺寸
  const newContainerWidth = container.clientWidth - 20 // 减去padding
  const newContainerHeight = container.clientHeight - 80 // 减去按钮和padding的高度
  // 计算理想尺寸
  const idealWidth =
    (maxDepth + 1) * (nodeConfig.width + nodeConfig.horizontalGap)

  // 计算需要的画布高度（考虑缩放比例）
  const requiredHeight =
    maxWidth * (nodeConfig.height + nodeConfig.verticalGap) * scale.value * 0.7 // 树结构高度×缩放比例

  // 更新画布尺寸
  canvasWidth.value = newContainerWidth
  canvasHeight.value = requiredHeight
}

// 绘制整个对阵图
const drawMatchTree = async () => {
  await nextTick()
  const canvas = canvasRef.value
  if (!canvas) return

  canvas.width = canvasWidth.value
  canvas.height = canvasHeight.value

  const ctx = canvas.getContext('2d')
  if (!ctx) return

  ctx.fillStyle = '#fff'
  ctx.fillRect(0, 0, canvas.width, canvas.height)

  const { maxDepth } = getTreeMetrics()
  const totalRoots = treeData.value.length
  if (totalRoots === 0) return

  // 计算多根节点的垂直分布
  // 基础间距根据树深度和根节点数量动态调整
  const baseSpacing = Math.max(
    nodeConfig.verticalGap * 2, // 最小间距
    (canvasHeight.value * 0.8) / (totalRoots * 1.2) // 根据根节点数量动态计算
  )
  const rootSpacing = baseSpacing * Math.pow(1.5, maxDepth - 1)
  const totalHeight = (totalRoots - 1) * rootSpacing
  const firstRootY = (canvasHeight.value - totalHeight) / 2

  // 绘制每个根节点及其子树
  treeData.value.forEach((node, index) => {
    const nodeY = firstRootY + index * rootSpacing
    drawTree(
      ctx,
      node,
      canvasWidth.value - nodeConfig.width - 40,
      nodeY,
      maxDepth - 1,
      maxDepth
    )
  })

  loading.value = false
}

// 保存为图片
const saveAsImage = () => {
  const canvas = canvasRef.value
  if (!canvas) return

  const link = document.createElement('a')
  link.download = '对阵图.png'
  link.href = canvas.toDataURL('image/png')
  link.click()
}

// 监听数据变化重新绘制
// watch(() => treeData.value, drawMatchTree, { deep: true })
const getTreeData = async () => {
  loading.value = true
  let res = await Promise.all([
    apiHandle[props.activeTab].queryPageList.post({
      pageNum: 1,
      pageSize: 100000,
      regNo: props.regNo,
      batchNo: props.batchNo,
      groupCategory: props.groupCategory,
      playType: props.playType,
      status: 'knockout',
    }),
    apiHandle[props.activeTab].queryPageList.post({
      pageNum: 1,
      pageSize: 2,
      regNo: props.regNo,
      batchNo: props.batchNo,
      groupCategory: props.groupCategory,
      playType: props.playType,
      status: 'final',
    }),
    apiHandle[props.activeTab].queryPageList.post({
      pageNum: 1,
      pageSize: 2,
      regNo: props.regNo,
      batchNo: props.batchNo,
      groupCategory: props.groupCategory,
      playType: props.playType,
      status: 'thirdPlace',
    }),
  ])
  loading.value = false
  if (res[0].code !== 200 || res[1].code !== 200 || res[2].code !== 200) {
    ElMessage.error('获取数据失败')
    tableData.value = []
    return
  }
  const concatArray = res[0].data.list.concat(res[1].data.list).reverse()
  const thirdPlace = res[2].data.list.reverse()

  // 处理数据，构建树结构
  let maxRound = 0,
    data = []
  const nodesIdMap = {},
    nodesGroupMap = {},
    roundData = {}
  concatArray.forEach((item) => {
    // 建立节点映射 key: knockoutRound-regNo 轮次-regNo
    const idKey = `${item.knockoutRound}-${item.regNo}`
    if (!nodesIdMap[idKey]) nodesIdMap[idKey] = item
    // 建立节点映射 key: knockoutRound-knockoutNo 轮次-淘汰赛组号
    const groupKey = `${item.knockoutRound}-${item.knockoutNo}`
    if (!nodesGroupMap[groupKey]) nodesGroupMap[groupKey] = []
    nodesGroupMap[groupKey].push(item)
    // 建立轮次映射 key: knockoutRound
    if (!roundData[item.knockoutRound]) roundData[item.knockoutRound] = []
    roundData[item.knockoutRound].push(item)
    if (item.knockoutRound > maxRound) maxRound = item.knockoutRound
  })
  // 从根节点开始向叶子节点构建树
  for (let i = maxRound; i >= 0; i--) {
    // 根节点 可能不止一个根节点
    if (data.length === 0) {
      data = roundData[i]
      continue
    }
    // 非根节点
    roundData[i].forEach((node) => {
      // 根据淘汰赛分组成员组号相同，通过节点映射 找到子节点
      const children = nodesGroupMap[`${node.knockoutRound}-${node.knockoutNo}`]
      const ids = children.map((child) => child.regNo)

      const parent = roundData[i + 1].find((item) => {
        return ids.includes(item.regNo)
      })

      parent.children = children
    })
  }
  // 补充预测数据
  if (data.length > 1) {
    const theFirst = data.find((item) => item.scoreRank === 1)
    if (theFirst) {
      data = [
        {
          ...theFirst,
          isWinner: true,
          scoreRank: '冠军',
          children: data,
        },
      ]
    } else {
      // 引用map
      const knockoutNoMap = {}
      data = data.reduce((pre, cur) => {
        if (!(cur.knockoutNo in knockoutNoMap)) {
          knockoutNoMap[cur.knockoutNo] = [cur]
          pre.push({
            knockoutNo: '?',
            isWinner: true,
            scoreRank:
              cur.scoreRank === 1
                ? '冠军'
                : cur.scoreRank === 2
                ? '亚军'
                : cur.scoreRank === 3
                ? '季军'
                : '',
            children: knockoutNoMap[cur.knockoutNo], // 利用引用赋值，将引用添加到pre数组中
          })
        } else {
          knockoutNoMap[cur.knockoutNo].push(cur) // 利用引用赋值，将引用添加到pre数组中
        }

        return pre
      }, [])
    }
  }

  treeData.value = data
  updateCanvasSize()
  drawMatchTree()
}

// 鼠标移动事件处理函数
const handleMouseMove = (canvas, event) => {
  if (!props.editable) return

  // 1. 获取原始鼠标坐标（不考虑缩放）
  const { x: rawX, y: rawY } = getMousePos(canvas, event)

  // 2. 转换为缩放后的逻辑坐标
  const mouseX = rawX / scale.value
  const mouseY = rawY / scale.value

  // 3. 统一计算起始位置（使用缩放后的坐标）
  const { maxDepth } = getTreeMetrics()
  const startX = canvasWidth.value / scale.value - nodeConfig.width - 40

  // 4. 多根节点垂直分布计算（与drawMatchTree保持一致）
  const totalRoots = treeData.value.length
  // 修改根节点间距计算，确保初期有足够空间
  const baseSpacing = Math.max(
    nodeConfig.verticalGap * 2,
    (canvasHeight.value * 0.8) / (totalRoots * 1.2),
    nodeConfig.height * 1 // 确保最小间距
  )
  const rootSpacing = baseSpacing * Math.pow(1.5, maxDepth - 1)
  const totalHeight = (totalRoots - 1) * rootSpacing
  const startY = (canvasHeight.value / scale.value - totalHeight) / 2

  // 检查悬停
  let found = false
  treeData.value.forEach((node, index) => {
    const nodeY = startY + index * rootSpacing
    const result = checkHoverNode(
      mouseX,
      mouseY,
      node,
      startX,
      nodeY, // 使用计算出的节点Y坐标
      maxDepth - 1
    )
    if (result) {
      found = true
      if (hoverNode.value?.node !== result.node) {
        hoverNode.value = result
        drawMatchTree()
      }
    }
  })

  if (!found && hoverNode.value) {
    hoverNode.value = null
    drawMatchTree()
  }
}

// 修改handleClick函数（同样逻辑）
const handleClick = (canvas, event) => {
  if (!props.editable) return

  // 使用与handleMouseMove完全相同的坐标计算逻辑
  const { x: rawX, y: rawY } = getMousePos(canvas, event)
  const mouseX = rawX / scale.value
  const mouseY = rawY / scale.value

  const { maxDepth } = getTreeMetrics()
  const startX = canvasWidth.value / scale.value - nodeConfig.width - 40

  const totalRoots = treeData.value.length
  const baseSpacing = Math.max(
    nodeConfig.verticalGap * 2,
    (canvasHeight.value * 0.8) / (totalRoots * 1.2)
  )
  const rootSpacing = baseSpacing * Math.pow(1.5, maxDepth - 1)
  const totalHeight = (totalRoots - 1) * rootSpacing
  const startY = (canvasHeight.value / scale.value - totalHeight) / 2

  treeData.value.forEach((node, index) => {
    const nodeY = startY + index * rootSpacing
    const result = checkHoverNode(
      mouseX,
      mouseY,
      node,
      startX,
      nodeY,
      maxDepth - 1
    )
    if (result) {
      emit('node-click', result.node)
      // ... 点击效果代码保持不变 ...
    }
  })
}

// 存储事件监听器引用
const eventListeners = ref({
  mousemove: null,
  click: null,
})

onMounted(() => {
  loading.value = false
  let resizeTimeout = null
  let isResizing = false

  const canvas = canvasRef.value
  if (canvas) {
    // 添加事件监听器并保存引用
    eventListeners.value.mousemove = (event) => handleMouseMove(canvas, event)
    eventListeners.value.click = (event) => handleClick(canvas, event)

    canvas.addEventListener('mousemove', eventListeners.value.mousemove)
    canvas.addEventListener('click', eventListeners.value.click)
  }

  // resizeObserver.value = new ResizeObserver((entries) => {
  //   if (isResizing) return
  //   const entry = entries[0]
  //   const newWidth = entry.contentRect.width
  //   const widthChange = Math.abs(newWidth - canvasWidth.value)

  //   if (widthChange < SIZE_CHANGE_THRESHOLD) {
  //     return
  //   }

  //   if (resizeTimeout) {
  //     clearTimeout(resizeTimeout)
  //   }

  //   resizeTimeout = setTimeout(() => {
  //     isResizing = true
  //     updateCanvasSize()
  //     nextTick(() => {
  //       drawMatchTree()
  //       isResizing = false
  //     })
  //   }, 150)
  // })

  // if (canvasRef.value?.parentElement) {
  //   resizeObserver.value.observe(canvasRef.value.parentElement)
  // }

  // 初始渲染
  // drawMatchTree()
})

// 组件卸载时清理所有事件监听器和观察器
onUnmounted(() => {
  const canvas = canvasRef.value
  if (canvas) {
    if (eventListeners.value.mousemove) {
      canvas.removeEventListener('mousemove', eventListeners.value.mousemove)
    }
    if (eventListeners.value.click) {
      canvas.removeEventListener('click', eventListeners.value.click)
    }
  }

  if (resizeObserver.value) {
    resizeObserver.value.disconnect()
    resizeObserver.value = null
  }
})

defineExpose({
  saveAsImage,
  getTreeData,
})
</script>

<style lang="scss" scoped>
.match-tree {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  padding: 10px;
  overflow: hidden;

  canvas {
    width: 100%;
    border: 1px solid #eee;
    margin-bottom: 20px;
  }
}
</style>
